package com.codebattery.config;

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShardingDataSourceConfig {

    @Bean("shardingMybatisSqlSessionFactory")
    public SqlSessionFactory shardingMybatisSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(
                shardingDatasource(getShardingRuleConfiguration(), order0Datasource(), order1Datasource()));
        String patternPath = ResourcePatternResolver.CLASSPATH_URL_PREFIX + "mapper/*Mapper.xml";
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(patternPath));
        sessionFactoryBean.getObject().getConfiguration()
                .setMapUnderscoreToCamelCase(true);
        return sessionFactoryBean.getObject();
    }

    @Bean(name = "order0Datasource")
    @ConfigurationProperties(prefix = "spring.shardingsphere.datasource.order0")
    public DataSource order0Datasource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "order1Datasource")
    @ConfigurationProperties(prefix = "spring.shardingsphere.datasource.order1")
    public DataSource order1Datasource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    public DataSource shardingDatasource(ShardingRuleConfiguration shardingRuleConf,
                                         DataSource order0Datasource,
                                         DataSource order1Datasource
                                         )
            throws SQLException {
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("order0", new DataSourceProxy(order0Datasource));
        dataSourceMap.put("order1", new DataSourceProxy(order1Datasource));
        return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConf,  new Properties());
    }


    private Properties additionalProperties() {
        Properties properties = new Properties();
//        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
//        properties.setProperty("hibernate.default_schema", "public");
        properties.setProperty("hibernate.dialect",
                "org.hibernate.dialect.MySQL5Dialect");
        properties.setProperty("hibernate.show_sql", "true");
        properties.setProperty("hibernate.hbm2ddl.auto", "none");
        return properties;
    }


    ShardingRuleConfiguration getShardingRuleConfiguration() {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
        Collection<String> bindingTableGroups = shardingRuleConfig.getBindingTableGroups();
        bindingTableGroups.add("t_order");
        bindingTableGroups.add("t_order_item");
//        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "order${user_id % 2}"));
//      shardingRuleConfig.setDefaultTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_$->{order_id % 4}"));

        return shardingRuleConfig;
    }

    private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() {
        KeyGeneratorConfiguration result = new KeyGeneratorConfiguration("SNOWFLAKE", "order_id");
        return result;
    }

    TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order",
                "order0.t_order_$->{[0,1,2,3]}, order1.t_order_$->{[0,1,2,3]}");
        result.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "order${user_id.toBigInteger() % 2}"));
        result.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_${order_id.toBigInteger() % 4}"));
        result.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        return result;
    }

    TableRuleConfiguration getOrderItemTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order_item",
                "order0.t_order_item_$->{[0,1,2,3]}, order1.t_order_item_$->{[0,1,2,3]}");
        result.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "order${user_id.toBigInteger() % 2}"));
        result.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_item_${order_id.toBigInteger() % 4}"));
        return result;
    }

}